"""
	Copyright 2008 Erik Volz

    This file is part of RDS Simulator.

    RDS Simulator is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    RDS Simulator is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.

erik.volz@gmail.com
http://www.erikvolz.info


front end to rdssim

TODO
0x get estimate of time to completion
0x button & methods for saving csv
0x revise message text
0x copyright
"""

from rdssim import *
from pylab import *

from Tkinter import * 
#Button, Entry, Label, Tk, StringVar,  END, Message, RAISED, RIDGE
from tkFileDialog import askdirectory,asksaveasfilename #
from tkMessageBox import showinfo,showerror

import exceptions, time

class App:
	# default values
	sampleSize = [250] # 
	targetFinishTime = 75
	numReplicates = 100  # known as numSimulations in  Parameters
	numSeeds = [8]
	recIntervalsScale = [7.]
	od = [0.40307102,0.21113244,0.17850288,0.20729367]
	
	"""
	"""
	def __init__(self, root):
		#
		frame = root.grid()
		self.root = root
		
		
		f = open('dialog1.txt', 'r')
		self.dialog1text = f.read(); f.close()
		
		f = open('dialog2.txt', 'r')
		self.dialog2text = f.read(); f.close()
		
		f = open('dialog3.txt', 'r')
		self.dialog3text = f.read(); f.close()
		
		f = open('dialog4.txt', 'r')
		self.dialog4text = f.read(); f.close()
		
		f = open('dialog4b.txt', 'r')
		self.dialog4btext = f.read(); f.close()
		
		self.parameters = Parameters()
		self.simulationSet = SimulationSet()
		
		self.__dialog1(root)
		#frame.pack()
	#
	
	def _clear_root(self):
		for widget in self.root.children.values():
			widget.grid_forget()
		#
		return None
	
	def __dialog1(self, root):
		""" first dialog handles target finish time, and #replicates """
		# DONE add paragraph of introductory text
		targetFinishTimeL = Label(root,text = 'Target Finish Time (days)'); 
		targetFinishTimeL.grid(padx=50, pady = 10, column=0, row=1)
		targetFinishTimeE = Entry(root); 
		targetFinishTimeE.grid(padx=50, pady=10,column=1, row=1)
		targetFinishTimeE.insert(0, repr(self.targetFinishTime))
		
		numReplicatesL =  Label(root,text = 'Number of Simulations'); 
		numReplicatesL.grid(padx=50, pady=10,column=0, row=2)
		numReplicatesE = Entry(root); 
		numReplicatesE.grid(padx=50,pady=10,column=1, row=2)
		numReplicatesE.insert(0, repr(self.numReplicates))
		
		continue1B = Button(root)
		continue1B['command'] = self.__continue1
		continue1B['text'] = 'Next'
		continue1B.grid(pady=25, column=0, row=3, columnspan=2);
		
		self.targetFinishTimeE = targetFinishTimeE; self.numReplicatesE = numReplicatesE
		
		message1 = Message(text=self.dialog1text, borderwidth=5, background='white', foreground='black', relief=RIDGE)
		message1.grid(column=0,row=0,columnspan=2)
	
	
	def __continue1(self):
		# load parameters
		goToDialog2 = True
		try:
			self.numReplicates = int(self.numReplicatesE.get())
			#self.sampleSize = int(self.sampleSize.get())
			self.targetFinishTime = int(self.targetFinishTimeE.get())
		except:
			goToDialog2 = False
			print 'Error in dialog 1 inputs'
			showerror("Step 1", "Error parsing input. Please enter integers > 0")
			self._clear_root()
			self.root.title('RDS Simulator, Step 1 of 4')
			self.__dialog1(root)
			#self.root.mainloop()
			
		#
		# go to next dialog
		if goToDialog2:
			self._clear_root()
			self.root.title('RDS Simulator, Step 2 of 4')
			self.__dialog2(root)
			
	################################
	
	def __dialog2(self,root):
		
		frame = root.grid()
		""" this is more challenging, as these are comma-delimited lists """
		numSeedsL = Label(root,text = 'Number of Seeds'); 
		numSeedsL.grid(padx=50,pady=10,column=0, row=1)
		numSeedsE = Entry(root); 
		numSeedsE.grid(padx=50,pady=10,column=1, row=1)
		numSeedsE.insert(0, repr(self.numSeeds)[1:-1])

		sampleSizeL = Label(root,text = 'Sample Size'); 
		sampleSizeL.grid(padx=50,pady=10,column=0, row=2)
		sampleSizeE = Entry(root); 
		sampleSizeE.grid(padx=50,pady=10,column=1, row=2)
		sampleSizeE.insert(0, repr(self.sampleSize)[1:-1])
		
		continue2B = Button(root, text = 'Next', command=self.__continue2); 
		continue2B.grid(pady=25,column=0, row=3, columnspan=2);
		
		self.numSeedsE = numSeedsE; self.sampleSizeE = sampleSizeE
		#frame.pack()
		
		message2 = Message(padx=50,pady=25,text=self.dialog2text, borderwidth=5, background='white', foreground='black', relief=RIDGE)
		message2.grid(padx=15,column=0,row=0,columnspan=2)

	#
	def __continue2(self):
		# load parameters
		goToDialog3 =True
		try:
			ns = self.numSeedsE.get()
			ns = ns.strip().split(',')
			numSeeds = [int(x) for x in ns] 
			for x in numSeeds:
				if x<=0:
					raise exceptions.Exception
				#
			#
			self.numSeeds = numSeeds
			
			ss = self.sampleSizeE.get()
			ss = ss.strip().split(',')
			sampleSize = [int(x) for x in ss]
			for x in sampleSize:
				if x <= 0:
					raise exceptions.Exception
				#
			#
			self.sampleSize = sampleSize;
		except:
			goToDialog3 = False
			print 'Error in dialog 2 inputs'
			showerror("Step 2", "Error parsing input. Please enter a comma-delimited list of integers > 0")
			self._clear_root()
			self.root.title('RDS Simulator, Step 2 of 4')
			self.__dialog2(root)
			
			#
		#
		# go to next dialog
		if goToDialog3:
			self._clear_root()
			self.root.title('RDS Simulator, Step 3 of 4')
			self.__dialog3(root)
		#
	#
	#
	###############################
	def __dialog3(self, root):
		frame = root.grid()
		odL = Label(root,text = 'Offspring Distribution'); 
		odL.grid(padx=50,pady=10,column=0, row=1)
		odE = Entry(root); 
		odE.grid(padx=50,pady=10,column=1, row=1)
		odE.insert(0, repr(self.od)[1:-1])

		recIntervalsScaleL = Label(root,text = 'Mean Recruitment Interval');
		recIntervalsScaleL.grid(padx=50,pady=10,column=0, row=2)
		recIntervalsScaleE = Entry(root); 
		recIntervalsScaleE.grid(padx=50,pady=10,column=1, row=2)
		recIntervalsScaleE.insert(0, repr(self.recIntervalsScale)[1:-1])
		
		continue3B = Button(root, text = 'Begin Simulations', command=self.__continue3); 
		continue3B.grid(pady=25,column=0, row=3, columnspan=2);
		
		self.odE = odE; self.recIntervalsScaleE = recIntervalsScaleE
		
		message3 = Message(padx=50,pady=25,text=self.dialog3text, borderwidth=5, background='white', foreground='black', relief=RIDGE)
		message3.grid(padx=15,column=0,row=0,columnspan=2)
		
		#frame.pack()
		#
	#
	def __continue3(self):
		gotodialog4 = True
		try:
			od = self.odE.get()
			od = od.strip().split(',')
			od = array([float(x) for x in od])
			od = od / sum(od)
			self.od = od.tolist()
			
			ris = self.recIntervalsScaleE.get()
			ris = ris.strip().split(',')
			ris = [float(x) for x in ris]
			for x in ris:
				if x <=0:
					raise exception.Exception
				#
			#
			self.recIntervalsScale = ris
		except:
			print 'Error in dialog 3 inputs'
			showerror("Step 3", "Error parsing input. Please enter a comma-delimited list of floating point numbers > 0")
			self._clear_root()
			self.root.title('RDS Simulator, Step 3 of 4')
			self.__dialog3(root)
			gotodialog4=False
			
		#		
		#self.__dialog4(root)		
		
		if gotodialog4:
			
			"""print self.sampleSize
			print self.targetFinishTime
			print self.numReplicates
			print self.numSeeds
			print self.recIntervalsScale 
			print self.od"""
			
			ss = SimulationSet(numSeeds=self.numSeeds,sampleSize=self.sampleSize,targetFinishTime=self.targetFinishTime,numSimulations=self.numReplicates,recIntervalsScale=self.recIntervalsScale,
			od=rv_discrete(values=(range(len(self.od)),self.od),
			name='offspring_distribution'))
			#od=rv_discrete(values=(range(4),[0.40307102,0.21113244,0.17850288,0.20729367]),
			#DONE this does not seem to set custom od values 
			
			self.simulationSet = ss
			
			self._clear_root()
			self.root.title('RDS Simulator, Step 4 of 4')
			self.__dialog4(root)
		#
		#DONE
		"""
		x run the simulations
		x open dialog instructing to wait for output
		x trigger output window    """
	#
	def __dialog4(self,root):
		frame = root.grid()
		# TODO add total \# simulations to output screen
		# TODO check if possible to abort simulations by closing window; if not, need a button...
		totalnumsims = self.numReplicates*len(self.numSeeds)*len(self.sampleSize)*len(self.recIntervalsScale)
		self.dialog4text+= 'You have requested a total of %i simulations.' % totalnumsims
		message4 = Message(padx=50,pady=25,text=self.dialog4text, borderwidth=5, background='white', foreground='black', relief=RIDGE)
		message4.grid(column=0,row=0,columnspan=3, padx=15, pady=15)
		# DONE cursor?
		self.root['cursor'] = 'watch'
		self.root.update() # refresh screen
		time.sleep(1) # pause so that screen can refresh
		try:
			print 'collect set start:', time.ctime()
			simsComplete = self.simulationSet.collect_set()
			print 'collect set finish:', time.ctime()
			outputString = self.simulationSet.print_output()
			self.root['cursor'] = ""
		except:
			#DONE dialog
			self.root['cursor'] = ""
			showerror("Step 4", "There was an unexpected error while running the simulations. Sorry!")
			raise
		#
		"""
		x text window with all the printed output
		x test size / function of compiled executable
		x buttons for generating graphs
			2 & 3d plots
				-mean of sampleSizeReachedOnTarget
				-mean of finishTimes
			histogram
				- finish times
		"""
		"""#DEBUG 
		k = self.simulationSet.finishTimes.keys()[0]
		hist(self.simulationSet.finishTimes[k])
		show()"""
		
		showinfo("","Simulations Complete")
		
		message4['text'] = self.dialog4btext
		
		make2db = Button(root, text = 'Plot Data 2D', command=self.__plot2d); 
		make2db.grid(pady=25, padx=10,column=1, row=2);
		
		#make3db = Button(root, text = 'Plot Data 3D', command=self.__plot3d); 
		#make3db.grid(pady=25, padx=10,column=2, row=2);
		
		make1db = Button(root, text = 'Plot Data', command=self.__plot1d); 
		make1db.grid(pady=25, padx=10,column=0, row=2);
		
		savecsvb = Button(root, text= 'Save Data', command=self.__save_csv)
		savecsvb.grid(pady=25,padx=10,column=2,row=2)
		
		self.root.update()
		
		osw = Toplevel(self.root) #output string window
		osw.title('Text Output')
		osSB = Scrollbar(osw); osSB.pack(side=RIGHT,fill=Y)
		ostext = Text(osw, yscrollcommand=osSB.set)
		ostext.pack(side=LEFT, fill=BOTH)
		osSB['command'] = ostext.yview
		ostext.insert(END, outputString)
		#ostext['state']= DISABLED
	#
	def __plot1d(self):
		p1d = Plot1d(self.root, self.simulationSet)
	
	def __plot2d(self):
		Plot2d(self.root, self.simulationSet)
	#
	
	def __save_csv(self):
		fn = asksaveasfilename()
		self.simulationSet.save_csv(fn)
#

class Plot1d:
	def __init__(self,root,simset):
		self.simulationSet = self.simset = simset
		self.root = root
		self.child = pw = Toplevel(self.root) #plot window
		pw.title('Plot Data')
		
		self.samplesizeLB = samplesizeLB = Listbox(pw, exportselection=0)
		for val in self.simset.sampleSize:
			samplesizeLB.insert(END, repr(val))
		#
		samplesizeL = Label(pw, text="Sample Size")
		
		self.numseedsLB = numseedsLB = Listbox(pw, exportselection=0)
		for val in self.simset.numSeeds:
			numseedsLB.insert(END, repr(val))
		#
		numseedsL = Label(pw, text="Number of Seeds")
		
		self.recscaleLB = recscaleLB = Listbox(pw, exportselection=0)
		for val in self.simset.recIntervalsScale:
			recscaleLB.insert(END, repr(val))
		#
		recscaleL = Label(pw, text="Recruitment Interval Scale")
		
		"""s = StringVar()
		s.set('')
		yaRB = Radiobutton(root, variable=s, text="Finish Time")"""
		
		goB = Button(pw, text='Plot', command=self.__plot)
		
		samplesizeL.grid(padx=10,row=0,column=0)
		samplesizeLB.grid(padx=10,row=1,column=0)
		numseedsL.grid(padx=10,row=0,column=1)
		numseedsLB.grid(padx=10,row=1,column=1)
		recscaleL.grid(padx=10,row=0,column=2)
		recscaleLB.grid(padx=10,row=1,column=2)
		goB.grid(row=3,column=0,columnspan=3,padx=10,pady=25)
	#
	
	def __plot(self):
		# make the key....
		""" ns = int(self.numseedsLB.curselection())
		ris = float(self.recscaleLB.curselection())
		ss = int(self.samplesizeLB.curselection())
		k=(self.simset.numSeeds.index(ns),self.simset.recIntervalsScale.index(ris), self.simset.sampleSize.index(ss)) """
		
		try:
			k = (int(self.numseedsLB.curselection()[0]),
			int(self.recscaleLB.curselection()[0]),
			int(self.samplesizeLB.curselection()[0])) #DONE this correct key?
		except:
			showerror("Plot 1 D", "Please select a value from each List Box")
			return False
		#
		
		# plot histogram of finish time
		d = self.simset.finishTimes[k]
		hold(True)
		h = hist(d, normed=True)
		m=max(h[0]); tft=self.simset.targetFinishTime; plot([tft,tft], [0,m], 'r-')
		xlabel('Time to sample completion (days)')
		ylabel('Frequency')
		show()
		return True
	#

class Plot2d:
	def __init__(self,root,simset):
		self.simulationSet = self.simset = simset
		self.root = root
		self.child = pw = Toplevel(self.root) #plot window
		pw.title('Plot Data')
		
		self.samplesizeLB = samplesizeLB = Listbox(pw, exportselection=0)
		for val in self.simset.sampleSize:
			samplesizeLB.insert(END, repr(val))
		#
		samplesizeL = Label(pw, text="Sample Size")
		
		self.numseedsLB = numseedsLB = Listbox(pw, exportselection=0)
		for val in self.simset.numSeeds:
			numseedsLB.insert(END, repr(val))
		#
		numseedsL = Label(pw, text="Number of Seeds")
		
		self.recscaleLB = recscaleLB = Listbox(pw, exportselection=0)
		for val in self.simset.recIntervalsScale:
			recscaleLB.insert(END, repr(val))
		#
		recscaleL = Label(pw, text="Recruitment Interval Scale")
		
		"""s = StringVar()
		s.set('')
		yaRB = Radiobutton(root, variable=s, text="Finish Time")"""
		
		goB = Button(pw, text='Plot', command=self.__plot)
		
		self.xaxis = xaxis = IntVar();
		samplesizeRB = Radiobutton(pw, text= "X-Axis = Sample Size", value=2,variable=xaxis)
		numseedsRB = Radiobutton(pw,text="X-Axis = Number of Seeds",value=0,variable=xaxis)
		recscaleRB = Radiobutton(pw,text="X-Axis = Recruitment Interval Scale",value=1,variable=xaxis)
		numseedsRB.select()
		
		samplesizeL.grid(padx=10,row=0,column=0)
		samplesizeLB.grid(padx=10,row=2,column=0)
		numseedsL.grid(padx=10,row=0,column=1)
		numseedsLB.grid(padx=10,row=2,column=1)
		recscaleL.grid(padx=10,row=0,column=2)
		recscaleLB.grid(padx=10,row=2,column=2)
		goB.grid(row=3,column=0,columnspan=3,padx=10,pady=25)
		samplesizeRB.grid(padx=10,row=1,column=0)
		numseedsRB.grid(padx=10,row=1,column=1)
		recscaleRB.grid(padx=10, row=1, column=2)
	#
	
	def __plot(self):
		# make the keys		
		try:
			xaxis = self.xaxis.get()
			if xaxis == 0:
				xaxisvar = self.simulationSet.numSeeds
				xlab = 'Number of Seeds'
				self.numseedsLB.select_clear(0,999)
				self.numseedsLB.select_set(0)
			elif xaxis == 1:
				xaxisvar = self.simulationSet.recIntervalsScale
				xlab = 'Recruitment Interval Scale'
				self.recscaleLB.select_clear(0,999)
				self.recscaleLB.select_set(0)
			elif xaxis == 2:
				xaxisvar = self.simulationSet.sampleSize
				xlab = 'Sample Size'
				self.samplesizeLB.select_clear(0,999)
				self.samplesizeLB.select_set(0)
			keys = [(int(self.numseedsLB.curselection()[0]),
			int(self.recscaleLB.curselection()[0]),
			int(self.samplesizeLB.curselection()[0]))]*len(xaxisvar)
			for i in range(len(xaxisvar)):
				kl = list(keys[i])
				kl[xaxis] = i
				keys[i] = tuple(kl)
			#
		except:
			showerror("Plot 2 D", "Please select a value from each List Box and a variable for the X-Axis.")
			return False
		#
		
		# plot histogram of finish time
		mft = [mean(self.simulationSet.finishTimes[k]) for k in keys]
		cbtft = [mean(self.simulationSet.sampleSizeReachedOnTarget[k]) for k in keys] # complete by target finsih time %
		figure(1)
		plot(xaxisvar, mft, 'bo', label='Mean Finish Time')
		ylabel('Mean Finish Time')
		xlabel(xlab)
		figure(2)
		plot(xaxisvar, cbtft, 'ro', label='% Complete by Finish Time')
		xlabel(xlab)
		ylabel('% Complete by Target Finish Time')
		
		show()
		
		return True
	#



if __name__=='__main__':
	#generateCoupons(2, '.')
	
	root = Tk()
	root.title('RDS Simulator, Step 1 of 4')
	app = App(root)
	#ss = SimulationSet(); p = Plot1d(root,ss)
	
	root.mainloop()
